# Find non Lazy Loaded Images outside of the viewport

List all images that don't have `loading="lazy"` or `[data-src]` _(lazy loading via JS)_ and are not in the viewport when the page loads. This script will help you find candidates for lazy loading by showing:

- Total number of images without lazy loading
- Total file size of all images
- Individual image details: URL, resolution (naturalWidth x naturalHeight), and file size

#### Snippet

```js copy
// Execute it after the page has loaded without any user interaction (Scroll, click, etc)
function isInViewport(tag) {
  let rect = tag.getBoundingClientRect();
  return (
    rect.bottom >= 0 &&
    rect.right >= 0 &&
    rect.top <= (window.innerHeight || document.documentElement.clientHeight) &&
    rect.left <= (window.innerWidth || document.documentElement.clientWidth)
  );
}

async function getImageSize(imgElement) {
  const src = imgElement.currentSrc || imgElement.src;

  if (!src || src.startsWith("data:")) {
    return 0;
  }

  try {
    const response = await fetch(src, { method: "HEAD" });
    const contentLength = response.headers.get("content-length");
    return contentLength ? parseInt(contentLength, 10) : 0;
  } catch (error) {
    console.warn(`Could not fetch size for: ${src}`, error);
    return 0;
  }
}

function formatBytes(bytes) {
  if (bytes === 0) return "0 Bytes";
  const k = 1024;
  const sizes = ["Bytes", "KB", "MB", "GB"];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + " " + sizes[i];
}

async function findImgCandidatesForLazyLoading() {
  let notLazyImages = document.querySelectorAll('img:not([data-src]):not([loading="lazy"])');
  const notLazyImagesOutOfViewport = Array.from(notLazyImages).filter((tag) => !isInViewport(tag));

  if (notLazyImagesOutOfViewport.length === 0) {
    console.log(
      `%c Good job, the site has all the images out of the viewport with lazyloading.`,
      "background: #222; color: lightgreen; padding: 0.5ch",
    );
    return;
  }

  console.log(
    `%c ⚠️ Found ${notLazyImagesOutOfViewport.length} images without lazy loading`,
    "background: #222; color: lightcoral; padding: 0.5ch; margin-top: 1em; font-weight: bold",
  );

  // Collect information from images
  const imagePromises = notLazyImagesOutOfViewport.map(async (img) => {
    const src = img.currentSrc || img.src;
    const size = await getImageSize(img);
    const width = img.naturalWidth;
    const height = img.naturalHeight;
    return { element: img, src, size, width, height };
  });

  const imagesData = await Promise.all(imagePromises);
  const totalSize = imagesData.reduce((sum, img) => sum + img.size, 0);

  // Mostrar resumen
  console.log(
    `%c 📊 Summary:`,
    "background: #222; color: #58a6ff; padding: 0.5ch; margin-top: 1em; font-weight: bold",
  );
  console.log(`   • Total images: ${notLazyImagesOutOfViewport.length}`);
  console.log(`   • Total size: ${formatBytes(totalSize)}`);

  // Display list of URLs with sizes and resolution
  console.log(
    `%c 📋 Image URLs:`,
    "background: #222; color: #58a6ff; padding: 0.5ch; margin-top: 1em; font-weight: bold",
  );

  imagesData.forEach((img, index) => {
    const sizeInfo = img.size > 0 ? `${formatBytes(img.size)}` : "size unknown";
    const resolution = `${img.width}x${img.height}`;
    console.log(`   ${index + 1}. ${img.src} (${resolution}, ${sizeInfo})`);
  });

  // Display items in console (for inspection)
  console.log(
    `%c 🔍 Image elements (click or hover to inspect):`,
    "background: #222; color: #58a6ff; padding: 0.5ch; margin-top: 1em; font-weight: bold",
  );
  notLazyImagesOutOfViewport.forEach((img) => console.log(img));
}

findImgCandidatesForLazyLoading();
```
